#include "liste.h"

void initializes_List(List *l, 
		      void (*create)(Element *, const Element e),
		      void (*delete)(Element *),
		      void (*print)(const Element),
		      int (*compare)(const Element, const Element)
		      )
{
  if(l)
    {
      l->adFirst = NULL;
      l->adLast = NULL;
      l->adCurrent = NULL;
      l->nbElement = 0;
      l->create = create;
      l->delete = delete;
      l->print = print;
      l->compare = compare;
    }
}

ListReturn initializesCopy_List(List *l1, const List *l2)
{
  if(l2->adFirst)
    {
      initializes_List(l1, l2->create, l2->delete, 
		      l2->print, l2->compare);
    }
  return assignment_List(l1,l2);
}

ListReturn isEmpty_List(const List *l)
{
  if(!l)
    {
      printf("la liste n'est pas initialisée");
      exit(EXIT_FAILURE);
    }
  if(l->adFirst == NULL)
    return LIST_EMPTY;
  return LIST_NOT_EMPTY;
}

ListReturn insertLast_List(const Element e , List *l)
{
  Cell *c = (Cell *) malloc (sizeof(Cell));
  if(c == NULL)
    {
      return LIST_ERR;
    }
  if(l->create == NULL)
    c->element = e;
  else
    l->create(&(c->element), e);
  c->next = NULL;
  if(l->adFirst == NULL)
    {
      l->adFirst = c;
      l->adCurrent = c;
      l->adLast = c;
    }
  else
    {
      l->adLast->next = c;
      l->adLast = c;
      l->adCurrent = c;
    }
  l->nbElement++;
  return LIST_SUCCESS;
}

ListReturn insertFirst_List(const Element e , List *l)
{
  Cell *c = (Cell *) malloc (sizeof(Cell));
  if(c == NULL)
    {
      fprintf(stderr, "La cellule n'a pas pu être initialisée en mémoire\n");
      return LIST_ERR;
    }
  if(l->create == NULL)
    c->element = e;
  else
    l->create(&(c->element), e);
  c->next = NULL;
  if(l->adFirst == NULL)
    {
      l->adFirst = c;
      l->adCurrent = c;
      l->adLast = c;
    }
  else
    {
      c->next = l->adFirst;
      l->adFirst = c;
      l->adCurrent = c;
    }
  l->nbElement++;
  return LIST_SUCCESS;
}

ListReturn insertCurrent_List(const Element e , List *l)
{
  Cell *c = NULL;
  int position = 0;
  c = (Cell *) malloc (sizeof(Cell));
  if(c == NULL)
    {
      return LIST_ERR;
    }
  if(l->create == NULL)
    c->element = e;
  else
    l->create(&(c->element), e);
  c->next = NULL;
  if(l->adFirst == NULL)
    {
      l->adFirst = c;
      l->adCurrent = c;
      l->adLast = c;
    }
  else if(l->adCurrent == NULL)
    {
      c->next = l->adFirst;
      l->adFirst = c;
      l->adCurrent = c;
    }
  else
    {
      Cell *cTmp = l->adFirst;

      if(cTmp != l->adCurrent)
	{
	  while(cTmp->next != l->adCurrent)
	    {
	      cTmp = cTmp->next;
	      position ++;
	    }
	  c->next = l->adCurrent;
	  cTmp->next = c;
	  l->adCurrent = c;
	}
      else
	{
	  c->next = l->adFirst;
	  l->adFirst = c;
	  l->adCurrent = c;
	}
    }
  l->nbElement++;
  return LIST_SUCCESS;
}

ListReturn assignment_List(List *l1, const List *l2)
{
  Cell *c = l2->adFirst;
  if(!isEmpty_List(l1))
    {
      clear_List(l1);
      initializes_List(l1, l2->create, l2->delete, 
		      l2->print, l2->compare);
    }
  while(c != NULL)
    {
      if(insertLast_List(c->element, l1) == 0)
	{
	  fprintf(stderr, "Une seule partie de la liste L2 à pu être copiée\n");
	  return LIST_ERR;
	}
      c = c->next;
    }
  return LIST_SUCCESS;
}

ListReturn deleteFirst_List(List *l)
{
  Cell *c = l->adFirst;
  if(c == NULL)
    return LIST_EMPTY;
  l->adFirst = c->next;
  if(l->adCurrent ==  c)
    {
      l->adCurrent = l->adFirst;
    }
  if(l->adLast == c)
    l->adLast = l->adFirst;
  l->nbElement--;
  if(l->delete != NULL)
    l->delete(&(c->element));
  free(c);
  return LIST_SUCCESS;
}

ListReturn deleteLast_List(List *l)
{
  Cell *c = l->adFirst;
  if(c == NULL)
    return LIST_EMPTY;
  if(l->adLast == NULL)
    return LIST_ERR;
  if(c == l->adLast)
    {
      return deleteFirst_List(l);
    }
  while(c->next != l->adLast)
    c = c->next;
  l->adLast = c;
  l->nbElement --;
  if(l->adCurrent == c->next)
    {
      l->adCurrent = l->adLast;
    }
  c = c->next;
  if(l->delete != NULL)
    l->delete(&(c->element));
  free(c);
  l->adLast->next = NULL;
  return LIST_SUCCESS;
}

ListReturn deleteCurrent_List(List *l)
{
  Cell *c = l->adFirst;
  Cell *cSupp;
  if(c == NULL)
    return LIST_EMPTY;
  if(l->adCurrent == NULL)
    return LIST_CUR_NULL;
  if(c == l->adCurrent)
    {
      return deleteFirst_List(l);
    }
  if(c == l->adLast)
    {
      return deleteLast_List(l);
    }
  cSupp = l->adCurrent;
  while(c->next != l->adCurrent)
    c = c->next;
  
  c->next = l->adCurrent->next;
  l->adCurrent = c;
  l->nbElement--;
  if(l->delete != NULL)
    l->delete(&(cSupp->element));
  free(cSupp);
  return LIST_SUCCESS;
}

ListReturn clear_List(List *l)
{
  int ret;
  while(!isEmpty_List(l))
    {
      ret = deleteFirst_List(l);
      if(ret == LIST_ERR)
	return ret;
    }
  return LIST_SUCCESS;
}

void testament_List(List *l)
{
  clear_List(l);
}

int print_List(const List *l)
{
  if(l)
    {
      Cell *c;
      if(l->print == NULL)
	{
	  fprintf(stderr, "Il n'y a pas de fonction d'affichage\n");
	  return LIST_ERR;
	}
      c = l->adFirst;
      while(c != NULL)
	{
	  l->print(c->element);
	  c = c->next;
	}
      printf("\n");
    }
  return LIST_SUCCESS;
}

ListReturn findElement_List(const Element e, List *l)
{
  if(!isEmpty_List(l))
    {
      Cell *c = l->adFirst;
      int position = 0;
      while(c != NULL)
	{
	  if(l->compare == NULL)
	    {
	      if(c->element == e)
		{
		  l->adCurrent = c;
		  return LIST_ERR;
		}
	    }
	  else if(l->compare(c->element, e))
	    {
	      l->adCurrent = c;
	      return LIST_SUCCESS;
	    }
	  else
	    {
	      c = c->next;
	      position ++;
	    }
	}
    }
  return LIST_NOT_FOUND;
}

Element getDataCurrent_List(const List *l)
{
  if(l->adCurrent != NULL)
    return l->adCurrent->element;
  return NULL;
}

Element getDataFirst_List(const List *l)
{
  if(l->adFirst != NULL)
    return l->adFirst->element;
  return NULL;
}

Element getDataLast_List(const List *l)
{
  if(l->adLast != NULL)
    return l->adLast->element;
  return NULL;
}

void first_List(List *l)
{
  l->adCurrent = l->adFirst;
}

ListReturn next_List(List *l)
{
  if(l->adCurrent == NULL)
    {
      l->adCurrent = l->adFirst;
      return LIST_CUR_FIRST;
    }
  l->adCurrent = l->adCurrent->next;
  if(l->adCurrent == NULL)
    return LIST_CUR_NULL;
  return LIST_SUCCESS;
}

ListReturn isLast_List(const List *l)
{
  if(l->adCurrent == l->adLast)
    return LIST_CUR_LAST;
  return LIST_CUR_NOT_LAST;
}

ListReturn deleteAt_List(List *l, const int indice)
{
  int i;
  Cell *cellAvant;
  Cell *cellSupp;
  if(indice < 0 || indice >= l->nbElement)
    return LIST_ERR;
  if(indice == 0)
    {
      return deleteFirst_List(l);
    }
  if(indice == l->nbElement - 1)
    {
      return deleteLast_List(l);
    }
  cellAvant = l->adFirst;
  for(i = 0; i < indice; i++)
    {
      cellAvant = cellAvant->next;
    }
  cellSupp = cellAvant->next;
  cellAvant->next = cellSupp->next;
  if(l->delete != NULL)
    l->delete(&(cellSupp->element));
  free(cellSupp);
  l->nbElement--;
  return LIST_SUCCESS;
}

ListReturn insertAt_List(const Element e, List *l, const int indice)
{
  int i;
  Cell *cell;
  Cell *cellNew;
  if(indice < 0 || indice > l->nbElement)
    {
      return LIST_ERR;
    }
  if(indice == 0 || l->adFirst == NULL)
    {
      return insertFirst_List(e, l);
    }
  if(indice == l->nbElement)
    {
      return insertLast_List(e, l);
    }
  cellNew = (Cell *) malloc (sizeof(Cell));
  if(cellNew == NULL)
    {
      fprintf(stderr, "La nouvelle cellule n'a pas pu être initialisé en mémoire\n");
      return LIST_ERR;
    }
  if(l->create == NULL)
    cellNew->element = e;
  else
    l->create(&(cellNew->element), e);
  
  cell = l->adFirst;
  for(i = 0; i < indice - 1; i++)
    {
      cell = cell->next;
    }
  cellNew->next = cell->next->next;
  cell->next = cellNew;
  l->nbElement ++;
  return LIST_SUCCESS;
}

Element getDataAt_List(List *l, const int indice)
{
  int i;
  if(indice < 0 || indice >= l->nbElement)
    return NULL;
  if(indice == l->nbElement - 1)
    return getDataLast_List(l);
  l->adCurrent = l->adFirst;
  for(i = 0; i < indice; i ++)
    l->adCurrent = l->adCurrent->next;
  return l->adCurrent->element;
}
